package nsqproc

import (
    "time"
	"errors"
	"strconv"
	"context"
    "wxsdk/mp/material"
	"wxsdk/miniapp/core"
    "encoding/json"
	"dhfshop/models/consts"
	"dhfshop/models/isv"
	"dhfshop/models/im"
	"dhfshop/models/member"
	"dhfshop/models/reserve"
	"dhfshop/models/shopping"
	"dhfshop/models/proto"
	"dhfshop/domain/im"
	"dhfshop/service/nsqcli"
	"dhfshop/service/event"
	"github.com/astaxie/beego"
	"github.com/smallnest/rpcx/client"
)

type NsqTimerMessage struct {
	Types    string `json:"types"`   
	Callback string `json:"callback_data"`
}

func NsqRegisterTimerFunc(body []byte) error {
	var nsqMsg NsqTimerMessage
	if err := json.Unmarshal(body, &nsqMsg); err != nil {
	   beego.Debug(err.Error())
	}
	beego.Debug("nsq message:", string(body))
	switch nsqMsg.Types {
	    case "TimeoutClosed":          //60分钟未付款，则关闭
		   //timeoutClosOrder(nsqMsg.Callback)
		case "TimeoutSign":            //7天自动确认收货
		    //timeoutSignOrder(nsqMsg.Callback)
		case "materialtimer":          //永久素材模拟
		    //materialHandle(nsqMsg.Callback)
		case "ReminderArrived":        //赴约提前一天提醒
		    //reminderArrived(nsqMsg.Callback)
		case "LoginInit":
		    //loginInitHandle(nsqMsg.Callback)
		case "DelExpiredFormId":
		    //delExpiredFormIdHandle(nsqMsg.Callback)
		default:
	}
	return nil
}

// 订单超30分钟不付款关闭并消息提醒
func timeoutClosOrder(recallMsg string) (err error) {
	type RecallOrderMsg struct {
		OrderId   int64 `json:"order_id"`
	}
	var msg RecallOrderMsg
	if err = json.Unmarshal([]byte(recallMsg), &msg); err != nil {
		beego.Debug("timeoutClosOrder:", err.Error())
		return err
	}
	orders, err := shopping.GetOrderById(msg.OrderId)
	if err != nil {
	    beego.Debug("timeoutClosOrder GetOrderById:", err.Error())
	    return
	}
	if orders.CreatedAt.Unix() - time.Now().UTC().Unix() < 28*60 {
	    beego.Debug("timeoutClosOrder: time is invalid")
		return
	}
	if orders.Status != "S_ORDER_WAIT_PAYMENT" {
	    beego.Debug("timeoutClosOrder status is invalid:", orders.Status)
		return
	}
	var result string = ""
	var req = new(event.TradeMsg)
	req.CompanyId = orders.PartnerId
	req.UserId = orders.MemberId
	req.OrderId = msg.OrderId
	req.MsgHeader.EventType = "E_ORDER_CANCEL"
	message, err := event.EncodeMsg(req)
	if err != nil {
		beego.Debug("timeoutClosOrder EncodeMsg Error:", err.Error())
		return      
	}
	event.KernelBus.Publish("E_ORDER_CANCEL",message,&result)
	if len(result) > 0 {
	    beego.Debug("E_ORDER_CANCEL:", result)
		err = errors.New(result)
		return
	}
	return
}

// 7天自动确认收货
func timeoutSignOrder(recallMsg string) (err error) {
	type RecallOrderMsg struct {
		OrderId   int64 `json:"order_id"`
	}
	var msg RecallOrderMsg
	if err = json.Unmarshal([]byte(recallMsg), &msg); err != nil {
		return err
	}
	orders, err := shopping.GetOrderById(msg.OrderId)
	if err != nil {
	    beego.Debug("GetOrderById:", err.Error())
	    return
	}
	if orders.CreatedAt.Unix() - time.Now().UTC().Unix() < 7*24*3500 {
	    return
	}
	if orders.Status != "S_ORDER_WAIT_RECEIVE" {
	    return
	}
	var result string = ""
	var req = new(event.TradeMsg)
	req.CompanyId = orders.PartnerId
	req.UserId = orders.MemberId
	req.OrderId = msg.OrderId
	req.MsgHeader.EventType = "E_ORDER_RECEIVE"
	message, err := event.EncodeMsg(req)
	if err != nil {
		return      
	}
	event.KernelBus.Publish("E_ORDER_RECEIVE",message,&result)
	if len(result) > 0 {
		beego.Debug("E_ORDER_RECEIVE:", result)
		err = errors.New(result)
		return
	}
	return
}

func materialHandle(callBack string) (err error) {
	body := struct {
		Types          string `json:"types"`
		MateSimuTaskId int64  `json:"mate_simu_task_id"`
	}{}
	if err = json.Unmarshal([]byte(callBack), &body); err != nil {
		return 
	}
	//beego.Debug("materialHandle:", string(callBack))
	mateSimuTask, e := isv.GetMateSimuTaskById(body.MateSimuTaskId)
	if e != nil {
	    err = e
		beego.Debug("GetMateSimuTaskById:", err.Error())
		return
	}
	if mateSimuTask.ExpiredAt.UTC().Unix() - time.Now().UTC().Unix() > 3*3600 {
	    beego.Debug("time more than 2 hours")
		return
	}
	custAccount, e := isv.GetCustAccountByAppId(mateSimuTask.AppId)
	if e != nil {
		err = e
		beego.Debug("GetCustAccountByAppId:", err.Error())
		return
	}
	imgData, e := core.GetCommonFileByHttp(mateSimuTask.LocalUrl)
	if e != nil {
	    err = e
		beego.Debug("GetCommonFileByHttp:", err.Error())
		return
	}
	media, e := material.MediaUploadByData(custAccount.AccessToken, "image", imgData)
	if e != nil {
		err = e
		beego.Debug("MediaUploadByData:", err.Error())
		return
	}
	// 更新media_id
	mateSimuTask.MediaId = media.MediaId
	mateSimuTask.CreatedAt = time.Unix(media.CreatedAt, 0).UTC()
	mateSimuTask.ExpiredAt = mateSimuTask.CreatedAt.Add(time.Hour * 24 * 3)
	beego.Debug("media.CreatedAt:",media.CreatedAt,"task.CreatedAt:",mateSimuTask.CreatedAt,"task.ExpiredAt:",mateSimuTask.ExpiredAt)
	if _, err = isv.UpdateMateSimuTaskById(mateSimuTask, "media_id", "created_at", "expired_at");err != nil {
		beego.Debug("UpdateMateSimuTaskById:", err.Error())
		return
	}
	// 切记给定时器重新建立任务
	types := "materialtimer"
	ttlSeconds := 70*3600
	timerBody := struct {
		Types          string `json:"types"`
		MateSimuTaskId int64  `json:"mate_simu_task_id"`
	}{Types:types, MateSimuTaskId: body.MateSimuTaskId}
	if callBackData, err := json.Marshal(&timerBody); err == nil {
		if err = RpcAddTimerTask(types, string(callBackData), ttlSeconds); err != nil {
		    beego.Debug("RpcAddTimerTask:", err.Error())
		}
	}
	return
}

// 赴约提前一天提醒
func reminderArrived(recallMsg string) (err error) {
	type RecallOrderMsg struct {
		ReservationId   int64 `json:"reservation_id"`
	}
	var msg RecallOrderMsg
	if err = json.Unmarshal([]byte(recallMsg), &msg); err != nil {
		return err
	}
	reservation, err := reserve.GetReservationById(msg.ReservationId)
	if err != nil {
	    beego.Debug("GetReservationById:", err.Error())
		return
	}
	members, err := member.GetMemberById(reservation.MemberId)
	if err != nil {
	    beego.Debug("GetMemberById:", err.Error())
		return
	}
	custAccount, err := isv.GetCustAccountByAppId(members.AppId)
	if err != nil {
		beego.Debug("GetCustAccountByAppId:", err.Error())
		return
	}
	templateId, err := imlayer.GetTemplateId(custAccount.PartnerId,members.AppId,"AT0240")
	if err != nil {
		beego.Debug("GetTemplateId:", err.Error())
		return
	}
	reservationIdStr := strconv.FormatInt(msg.ReservationId, 10)
	formId, err := im.GetFormIdByOpenId(members.OpenID, "yuyue", reservationIdStr)
	if err != nil {
		beego.Debug("GetFormIdByOpenId:", err.Error())
		return
	}			    
	page := ""
	sortNumber := strconv.Itoa(reservation.OrderSeq)
	sourceTime := reservation.SourceAt.Format(consts.DATE_FORMAT)
	err = imlayer.ReserveArriveTempMsg(custAccount.AccessToken,page,members.OpenID,templateId,
	                           formId,reservation.Schema.Name,sortNumber,sourceTime)
	if err != nil {
		beego.Debug("ReserveArriveTempMsg:", err.Error())
	}
	return
}

func loginInitHandle(callBack string) (err error) {
	body := struct {
		PartnerId int64  `json:"partner_id"`
		AppId     string `json:"app_id"`
	}{}
	if err = json.Unmarshal([]byte(callBack), &body); err != nil {
		return 
	}
	serviceTypeId := 2
	if custAccount, err := isv.GetCustAccountByAppId(body.AppId); err == nil {
	    serviceTypeId = custAccount.ServiceTypeId
	}
	im.InitDefaultAutoRspCfg(body.PartnerId, serviceTypeId, body.AppId)  //默认创建为小程序类型
	imlayer.InitDefaultTemplateCfg(body.PartnerId, body.AppId)
	member.InitDefaultAccountConf(body.PartnerId)
	reserve.InitDefaultReserveOpt(body.PartnerId)
	return
}

func delExpiredFormIdHandle(callBack string) (err error) {
	body := struct {
		PartnerId int64  `json:"partner_id"`
		AppId     string `json:"app_id"`
	}{}
	if err = json.Unmarshal([]byte(callBack), &body); err != nil {
		return 
	}
	if err = im.ClearExpired(); err != nil {
	    beego.Debug(err.Error())
	}
	return
}

func RpcAddTimerTask(types, callBackData string, ttlSeconds int) (err error) {
    nsqUrl := beego.AppConfig.String("NsqUrl")
	nsqTimerTopic := beego.AppConfig.String("NsqTimerTopic")
	rpcAddr := beego.AppConfig.String("TimerRpcAddr")
	d := client.NewPeer2PeerDiscovery("tcp@" + rpcAddr, "")
	xclient := client.NewXClient("TimerApi", client.Failtry, client.RandomSelect, d, client.DefaultOption)
	defer xclient.Close()
	args := &proto.AddTaskArgs{
		Types: types,
		CallbackAddr: nsqUrl,
		CallbackTopic: nsqTimerTopic,
		CallbackData: callBackData,
		TTLSeconds: int32(ttlSeconds),
	}
	reply := &proto.TimerReply{}
	_, err = xclient.Go(context.Background(), "AddTimerTask", args, reply, nil)
    return
}

func CreateLoinInitTask(partnerId int64) (err error) {
    nsqUrl := beego.AppConfig.String("NsqUrl")
	nsqTimerTopic := beego.AppConfig.String("NsqTimerTopic")
	callbackBody := struct {
		PartnerId int64  `json:"partner_id"`
		AppId     string `json:"app_id"`
	}{PartnerId:partnerId}
	accounts, e := isv.QueryCustAccountsByPartnerId(partnerId, 0, 100)
	if e != nil {
	   err = e
	   return
	} 
	for _, account := range accounts {
		callbackBody.AppId = account.AppId
		if callBackJson, e := json.Marshal(&callbackBody); e == nil {
			nsqMsg := NsqTimerMessage{Types:"LoginInit",Callback:string(callBackJson)}
			if nsqMsgJson, e := json.Marshal(&nsqMsg); e == nil {
				if err = nsqcli.StartProducer(nsqUrl, nsqTimerTopic, nsqMsgJson); err != nil {
					beego.Debug("StartProducer err:", err.Error())
				}
			}
		}
	}
    return
}

func DelFormIdExpiredTask(partnerId int64) (err error) {
    nsqUrl := beego.AppConfig.String("NsqUrl")
	nsqTimerTopic := beego.AppConfig.String("NsqTimerTopic")
	callbackBody := struct {
		PartnerId int64  `json:"partner_id"`
		AppId     string `json:"app_id"`
	}{PartnerId:partnerId,AppId:""}	
	if callBackJson, e := json.Marshal(&callbackBody); e == nil {
		nsqMsg := NsqTimerMessage{Types:"DelExpiredFormId",Callback:string(callBackJson)}
		if nsqMsgJson, e := json.Marshal(&nsqMsg); e == nil {
			if err = nsqcli.StartProducer(nsqUrl, nsqTimerTopic, nsqMsgJson); err != nil {
				beego.Debug("StartProducer err:", err.Error())
			}
		}
	}
    return
}
